package encsearch

import (
	"fhe-search-cloud/encdb"
	"fhe-search-cloud/g"
	"fhe-search-cloud/models"
	"fhe-search-cloud/utils"
	"fmt"
	"sort"
	"strconv"
	"strings"

	"gitee.com/fhe-search/fhe-search-rpc/encrpcclient"
)

// StringFheEncSearch 输入一个搜索串， 得到密文信息列表
func StringFheEncSearch(searchStr string) []*models.EncDocInfoItem {
	cutter := g.GetCutter()
	termArr := cutter.CutForSearch(searchStr, true)
	const MaxTermNum = 10
	// 控制关键词的数量
	if len(termArr) > MaxTermNum {
		sort.Slice(termArr, func(i, j int) bool {
			return len(termArr[i]) > len(termArr[j])
		})
		termArr = termArr[:MaxTermNum]
	}
	termSet := make(map[string]struct{})
	for _, item := range termArr {
		termSet[item] = struct{}{}
	}
	encDocInfoItemMp := make(map[int]*models.EncDocInfoItem)
	//// 多关键词并行检索
	//encDocInfoItemMpMutex := sync.RWMutex{}
	//for term := range termSet {
	//	go func() {
	//		encDocInfoItemArr := OneTermFheEncSearch(term)
	//		// merge encDocInfoItemArr to encDocInfoItemMp
	//		for _, item := range encDocInfoItemArr {
	//			docId := item.DocId
	//			tfidf := item.TFIDF
	//			encDocInfoItemMpMutex.Lock()
	//			if _, ok := encDocInfoItemMp[docId]; ok {
	//				encDocInfoItemMp[docId].TFIDF += tfidf
	//			} else {
	//				encDocInfoItemMp[docId] = item
	//			}
	//			encDocInfoItemMpMutex.Unlock()
	//		}
	//	}()
	//}
	// 多关键词串行检索
	for term := range termSet {
		encDocInfoItemArr := OneTermFheEncSearch(term)
		// merge encDocInfoItemArr to encDocInfoItemMp
		for _, item := range encDocInfoItemArr {
			docId := item.DocId
			tfidf := item.TFIDF
			if _, ok := encDocInfoItemMp[docId]; ok {
				encDocInfoItemMp[docId].TFIDF *= tfidf
			} else {
				encDocInfoItemMp[docId] = item
			}
		}
	}
	encDocInfoArr := make([]*models.EncDocInfoItem, 0)
	for _, item := range encDocInfoItemMp {
		encDocInfoArr = append(encDocInfoArr, item)
	}
	// 根据tfidf排序
	sort.Slice(encDocInfoArr, func(i, j int) bool {
		return encDocInfoArr[i].TFIDF > encDocInfoArr[j].TFIDF
	})
	return encDocInfoArr
}

// OneTermFheEncSearch 输入单关键词，获得密文信息列表
func OneTermFheEncSearch(searchTerm string) []*models.EncDocInfoItem {
	termIdArr := GetTermId(searchTerm)
	if len(termIdArr) == 0 {
		return nil
	}
	termLen := utils.CharNum(searchTerm)
	// 根据term_id查index1表，获取encDocInfoItemArr
	termIdStrArr := make([]string, len(termIdArr))
	for i := 0; i < len(termIdArr); i++ {
		termIdStrArr[i] = strconv.Itoa(termIdArr[i])
	}
	termIdsStr := strings.Join(termIdStrArr, ",")
	var encDocInfoItemArr []*models.EncDocInfoItem
	sql := fmt.Sprintf(`select doc_id,enc_freq, enc_title, enc_file_type, enc_file_path from index1 ind
join enc_documents ed on ind.doc_id = ed.id
where ind.term_id in (%s) and ind.term_len=%d`,
		termIdsStr, termLen)
	/*
		sql1
		select doc_id,enc_freq, enc_title, enc_file_path
		from index1 ind
		join enc_documents ed on ind.doc_id = ed.id
		where ind.term_id in (%s) and ind.term_len=%d
		sql2
		select doc_id,enc_freq, enc_title, enc_file_path
		from (select doc_id, enc_freq from index1 where term_id in (%s) and term_len=%d) as ind
		join enc_documents ed on ind.doc_id = ed.id
		sql1和sql2执行起来没区别
	*/
	fmt.Println(sql)
	err := encdb.GetEncDBConn().Select(&encDocInfoItemArr, sql)
	if err != nil {
		panic(err)
	}
	// 根据encDocInfoItemArr，计算出对应的tfidf，并解密标题
	for i := 0; i < len(encDocInfoItemArr); i++ {
		// 解密EncFreq
		// todo: 批量解密EncFreq
		// todo: 解密这里需要发送给加密服务器
		freq := int32(0)
		freq, err = encrpcclient.SymDecS32(encDocInfoItemArr[i].EncFreq)
		if err != nil {
			panic(err)
		}
		encDocInfoItemArr[i].Freq = int(freq)
		encDocInfoItemArr[i].SetTFIDF(len(encDocInfoItemArr))
		encDocInfoItemArr[i].Title, err = encrpcclient.SymDecUtf8(encDocInfoItemArr[i].EncTitle)
		if err != nil {
			panic(err)
		}
		encDocInfoItemArr[i].FileType, err = encrpcclient.SymDecUtf8(encDocInfoItemArr[i].EncFileType)
		if err != nil {
			panic(err)
		}
	}
	return encDocInfoItemArr
}

func GetTermId(term string) []int {
	// 根据桶编号缩小同态减法的范围
	bucket8 := encrpcclient.GetBucket8(term)
	termLen := utils.CharNum(term)
	fheEncText, err := encrpcclient.FheEncUtf8(term)
	if err != nil {
		panic(err)
	}
	sql := fmt.Sprintf("select term_id, fhe_term from term%d where bucket8 = %d", termLen, bucket8)
	fmt.Println(sql)
	termResult := make([]models.TermItem, 0)
	err = encdb.GetEncDBConn().Select(&termResult, sql)
	if err != nil {
		panic(err)
	}
	encArr := make([][]byte, len(termResult))
	for i := 0; i < len(termResult); i++ {
		encArr[i] = termResult[i].FheTerm
	}
	fmt.Println("encArr len:", len(encArr))
	// 桶内同态减法
	encDiffArr := make([][]byte, len(termResult))
	for i := 0; i < len(encArr); i++ {
		if len(encArr[i]) != len(fheEncText) {
			continue
		}
		encDiffArr[i], err = encrpcclient.FheSub(fheEncText, encArr[i])
		if err != nil {
			panic(err)
		}
	}
	// 解密密差，解密结果为0即表示匹配成功，获取匹配成功的term_id
	// todo: 解密这里需要发送给加密服务器
	termIdArr := make([]int, 0)
	for i := 0; i < len(encDiffArr); i++ {
		flag, errInner := encrpcclient.FheDecDiff(encDiffArr[i])
		if errInner != nil {
			panic(errInner)
		}
		if flag {
			termIdArr = append(termIdArr, termResult[i].TermId)
		}
	}
	return termIdArr
}

// GetPlainTextByDocId 根据文档ID，获取文件的base64编码
func GetPlainTextByDocId(docId int) string {
	var encDoc models.EncDocument
	sql := fmt.Sprintf("select * from enc_documents where id = %d", docId)
	err := encdb.GetEncDBConn().Get(&encDoc, sql)
	if err != nil {
		panic(err)
	}
	fileBasePath := g.GetConfig().FheSearchConfig.FileBasePath
	encFilePath := fileBasePath + encDoc.EncFilePath
	contentBase64, err := ReadEncFile(encFilePath)
	if err != nil {
		panic(err)
	}
	return contentBase64
}
